#include <iostream>

#include "gtest/gtest.h"

#include <utils/Log.h>
#include <utils/Trace.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/Surface.h>
#include <EGL/egl.h>
#include <GLES/gl.h>
#include <GLES2/gl2.h>

#include "gpu/GrDirectContext.h"
#include "gpu/gl/GrGLInterface.h"
#include "core/SkData.h"
#include "core/SkImage.h"
#include "core/SkStream.h"
#include "core/SkCanvas.h"
#include "core/SkSurface.h"
#include "core/SkBitmap.h"
#include "core/SkImageEncoder.h"
#include "core/SkTileMode.h"
#include "core/SkFont.h"
#include "core/SkShader.h"
#include "core/SkTextBlob.h"
#include "core/SkRRect.h"
#include "encode/SkPngEncoder.h"

using namespace android;
using Transaction = SurfaceComposerClient::Transaction;

class TestMain : public testing::Test {
public:
    sk_sp<SkSurface> surface;
    SkCanvas* canvas;
    sk_sp<SkImage> image;

    TestMain() : Test() {

    }

    ~TestMain() override {

    }

    void SetUp() override {
        initOpenGL();
        initGpuSurface();
    }

    void TearDown() override {

    }

    void initGpuSurface() {
        sk_sp<const GrGLInterface> interface = nullptr;
        sk_sp<GrDirectContext> context = GrDirectContext::MakeGL(interface);
        SkImageInfo info = SkImageInfo:: MakeN32Premul(400, 300);
        surface = SkSurface::MakeRenderTarget(context.get(), skgpu::Budgeted::kNo, info);
        canvas = surface->getCanvas();
    }

    void initOpenGL() {
        ATRACE_NAME("CreateWindowSurface");
        uint32_t width = 400;
        uint32_t height = 300;
        sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient;
        sp<SurfaceControl> surfaceControl = composerClient->createSurface(
                    String8("Hello World Surface"), width, height,
                    PIXEL_FORMAT_RGB_888, 0);
        Transaction t;
        t.setLayer(surfaceControl, 0x7FFFFFFF)
            .show(surfaceControl)
            .apply();
        sp<ANativeWindow> window = surfaceControl->getSurface();
        ATRACE_END();

        ATRACE_NAME("Init EGL/GL");
        EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        EGLint majorVersion;
        EGLint minorVersion;
        EGLConfig  glConfig;
        eglInitialize(eglDisplay, &majorVersion, &minorVersion);
        EGLint numConfigs = 0;
        const EGLint defaultConfigAttribs[] = {
            EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_DEPTH_SIZE, 16,
            EGL_STENCIL_SIZE, 8,
            EGL_NONE };
        eglChooseConfig(eglDisplay, defaultConfigAttribs, &glConfig, 1, &numConfigs);

        EGLSurface eglSurface = eglCreateWindowSurface(eglDisplay, glConfig, window.get(), nullptr);

        const EGLint defaultContextAttribs[] = {
            EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL_NONE };

        EGLContext eglContext = eglCreateContext(eglDisplay, glConfig, EGL_NO_CONTEXT, defaultContextAttribs);
        eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);

        EGLint w, h;
        eglQuerySurface(eglDisplay, eglSurface, EGL_WIDTH, &w);
        eglQuerySurface(eglDisplay, eglSurface, EGL_HEIGHT, &h);
        ALOGD("EGLSurface: w=%d, h=%d", w, h);

        glViewport(0, 0, w, h);

        GLubyte* vendor = (GLubyte*)glGetString(GL_VENDOR);
        GLubyte* version = (GLubyte*)glGetString(GL_VERSION);
        GLubyte* renderer = (GLubyte*)glGetString(GL_RENDERER);
        GLubyte* glslVersion = (GLubyte*)glGetString(GL_SHADING_LANGUAGE_VERSION);
        ALOGD("OpenGL INFO:");
        ALOGD("|-Vendor: %s", vendor);
        ALOGD("|-Version: %s", version);
        ALOGD("|-GLSL Version: %s", glslVersion);
        ALOGD("|-Renderer: %s", renderer);
        ALOGD("\n");
        ATRACE_END();
    }

    void WriteImageToFile(sk_sp<SkImage> image, std::string file) {
        std::vector<int32_t> pixels;
        pixels.resize(image->height() * image->width() * 4);
        SkPixmap skpixmap(SkImageInfo::Make(image->width(), image->height(), kN32_SkColorType,
                image->alphaType()), (const void*) &pixels.front(), image->width() * 4);
        if (!image->readPixels(skpixmap, 0, 0)) {
            ALOGE("Failed to read pixel from SkImage to SkPixmap.");
        } else {
            ALOGD("Successfully read pixel from SkImage to SkPixmap.");
        }

        SkString filename_pixmap;
        filename_pixmap.printf("/sdcard/Download/%s.png", file.c_str());
        SkFILEWStream outStream_pixmap(filename_pixmap.c_str());
        if (SkPngEncoder::Encode(&outStream_pixmap, skpixmap, SkPngEncoder::Options())) {
            outStream_pixmap.flush();
            ALOGI("Successfully save %s file.", filename_pixmap.c_str());
        } else {
            ALOGE("Failed to save to file.");
        }
    }
};

TEST_F(TestMain, testcase_01) {
    canvas->save();
    canvas->translate(SkIntToScalar(128), SkIntToScalar(128));
    canvas->rotate(SkIntToScalar(45));
    SkRect rect = SkRect::MakeXYWH(-90.5f, -90.5f, 181.0f, 181.0f);
    SkPaint paint;
    paint.setColor(SK_ColorBLUE);
    canvas->drawRect(rect, paint);
    canvas->restore();

    image = surface->makeImageSnapshot();
    WriteImageToFile(image, "testcase_01");
}

TEST_F(TestMain, testcase_02) {
    canvas->drawColor(SK_ColorWHITE);

    SkPaint paint;
    paint.setStyle(SkPaint::kStroke_Style);
    paint.setStrokeWidth(4);
    paint.setColor(SK_ColorRED);

    SkRect rect = SkRect::MakeXYWH(50, 50, 40, 60);
    canvas->drawRect(rect, paint);

    SkRRect oval;
    oval.setOval(rect);
    oval.offset(40, 60);
    paint.setColor(SK_ColorBLUE);
    canvas->drawRRect(oval, paint);

    paint.setColor(SK_ColorCYAN);
    canvas->drawCircle(180, 50, 25, paint);

    rect.offset(80, 0);
    paint.setColor(SK_ColorYELLOW);
    canvas->drawRoundRect(rect, 10, 10, paint);

    SkPath path;
    path.cubicTo(768, 0, -512, 256, 256, 256);
    paint.setColor(SK_ColorGREEN);
    canvas->drawPath(path, paint);

    canvas->drawImage(image, 128, 128, SkSamplingOptions(), &paint);

    SkRect rect2 = SkRect::MakeXYWH(0, 0, 40, 60);
    canvas->drawImageRect(image, rect2, SkSamplingOptions(), &paint);

    SkPaint paint2;
    auto text = SkTextBlob::MakeFromString("Hello, Skia!", SkFont(nullptr, 18));
    canvas->drawTextBlob(text.get(), 50, 25, paint2);

    image = surface->makeImageSnapshot();
    WriteImageToFile(image, "testcase_02");
}

TEST_F(TestMain, testcase_03) {
    const SkScalar scale = 256.0f;
    const SkScalar R = 0.45f * scale;
    const SkScalar TAU = 6.2831853f;
    SkPath path;
    path.moveTo(R, 0.0f);
    for (int i = 1; i < 7; ++i) {
        SkScalar theta = 3 * i * TAU / 7;
        path.lineTo(R * cos(theta), R * sin(theta));
    }
    path.close();
    SkPaint p;
    p.setAntiAlias(true);
    canvas->clear(SK_ColorWHITE);
    canvas->translate(0.5f * scale, 0.5f * scale);
    canvas->drawPath(path, p);

    image = surface->makeImageSnapshot();
    WriteImageToFile(image, "testcase_03");

}
